#include "hdl_i2c.h"


int8_t hdl_i2c_check_and_clr_err(I2C_Type *base,uint8_t sta)
{
	if(sta & (1<<4))//check if ARBITRATION lost
	{
		base->I2SR &= ~(1<<4);
		base->I2CR &= ~(1<<7);
		base->I2CR |= (1<<7);
		return I2C_STA_ARBITRATION_LOST;
	}
	else if(sta & (1<<0))//check if ARBITRATION lost
	{
		return I2C_STA_NAK;
	}
	return I2C_STA_OK;
}

int8_t hdl_i2c_start(I2C_Type *base,uint8_t dev_addr,HDL_I2C_TRANSFER_DIR_TYPEDEF dir)
{
	if(base->I2SR & (1<<5)) //i2c busy
	{
		return I2C_STA_BUSY;
	}

	base->I2CR |= (1<<5) | (1<<4);	//master,send

	base->I2DR = (dev_addr << 1) | (dir == i2c_trans_read?1:0);	//start signal
	return SUCCESS;
}

int8_t hdl_i2c_stop(I2C_Type *base)
{
	unsigned short timeout = 0xffff;
	base->I2CR &= ~((1 << 5) | (1 << 4) | (1 << 3));
	while((base->I2SR & (1 << 5)))
	{
		timeout--;
		if(timeout == 0)	/* 超时跳出 */
			return I2C_STA_TIMEOUT;
	}
	return SUCCESS;
}

int8_t hdl_i2c_restart(I2C_Type *base,uint8_t dev_addr,HDL_I2C_TRANSFER_DIR_TYPEDEF dir)
{
	if(((base->I2SR) & (1<<5)) && (((base->I2CR) & (1<<5)) == 0)) //i2c busy or slave mode
	{
		return I2C_STA_BUSY;
	}

	base->I2CR |= (1<<2) | (1<<4);	//master,send

	base->I2DR = (dev_addr << 1) | (dir == i2c_trans_read?1:0);	//start signal
	return SUCCESS;
}

int8_t hdl_i2c_master_write(I2C_Type *base,const uint8_t *w_dat,uint8_t len)
{
	while(!(base->I2SR & (1<<7)));	//wait transfer over
	base->I2SR &= ~(1<<1);

	base->I2CR |=(1<<4);

	while(len--)
	{
		base->I2DR = *w_dat++;
		while(!(base->I2SR & (1<<1))); //wait now send over
		base->I2SR &= ~(1<<1);

		if(hdl_i2c_check_and_clr_err(base,base->I2SR))
		{
			break;
		}

	}
	base->I2SR &= ~(1<<1);
	hdl_i2c_stop(base);
	return SUCCESS;
}

int8_t hdl_i2c_master_read(I2C_Type *base,uint8_t *r_dat,uint8_t len)
{
	volatile uint8_t dummy = 0;
	dummy ++;
	while(!(base->I2SR & (1<<7)));	//wait transfer over

	base->I2SR &= ~(1<<1);

	base->I2CR &= ~((1<<3)|(1<<4));

	if(len == 1)
	{
		base->I2CR |= 1<<3;
	}
	dummy = base->I2DR;

	while(len--)
	{
		while(!(base->I2SR & (1<<1))); //wait transfer over
		base->I2SR &= ~(1<<1);
		if(len == 0)
		{
			hdl_i2c_stop(base);
		}
		if(len == 1)
		{
			base->I2CR |= (1<<3); //no ack
		}
		*r_dat = base->I2DR;
		r_dat++;
	}
	return SUCCESS;
}

int8_t i2c_master_transfer(I2C_Type *base,hdl_i2c_transfer_StructDef *xfer)
{
	uint8_t ret = 0;
	uint16_t i2c_timeout_cnt = 0xffff;
	enum _i2c_direction direction = xfer->direction;	

	base->I2SR &= ~((1 << 1) | (1 << 4));			/* 清除标志位 */

	/* 等待传输完成 */
	while(!((base->I2SR >> 7) & 0X1)){}; 

	/* 如果是读的话，要先发送寄存器地址，所以要先将方向改为写 */
    if ((xfer->subaddressSize > 0) && (xfer->direction == i2c_trans_read))
    {
        direction = i2c_trans_write;
    }

    if(hdl_i2c_start(base, xfer->slaveAddress, direction))
    {	
		return ret;
	}

	while(!(base->I2SR & (1 << 1)))
	{
		if(i2c_timeout_cnt-- == 0)
		{
			DBG_ERR("I2C timeout\r\n");
			return;
		}
	}

    ret = hdl_i2c_check_and_clr_err(base, base->I2SR);	/* 检查是否出现传输错误 */
    if(ret)
    {
      	hdl_i2c_stop(base); 						/* 发送出错，发送停止信号 */
        return ret;
    }
	
    /* 发送寄存器地址 */
    if(xfer->subaddressSize)
    {
        do
        {
			base->I2SR &= ~(1 << 1);			/* 清除标志位 */
            xfer->subaddressSize--;				/* 地址长度减一 */
			
            base->I2DR =  ((xfer->subaddress) >> (8 * xfer->subaddressSize)); //向I2DR寄存器写入子地址
  
			while(!(base->I2SR & (1 << 1)));  	/* 等待传输完成 */

            /* 检查是否有错误发生 */
            ret = hdl_i2c_check_and_clr_err(base, base->I2SR);
            if(ret)
            {
             	hdl_i2c_stop(base); 				/* 发送停止信号 */
             	return ret;
            }  
        } while ((xfer->subaddressSize > 0) && (ret == I2C_STA_OK));

        if(xfer->direction == i2c_trans_read) 		/* 读取数据 */
        {
            base->I2SR &= ~(1 << 1);			/* 清除中断挂起位 */
            hdl_i2c_restart(base, xfer->slaveAddress, i2c_trans_read); /* 发送重复开始信号和从机地址 */
    		while(!(base->I2SR & (1 << 1))){};/* 等待传输完成 */

            /* 检查是否有错误发生 */
			ret = hdl_i2c_check_and_clr_err(base, base->I2SR);
            if(ret)
            {
             	ret = I2C_STA_ADDRANK;
                hdl_i2c_stop(base); 		/* 发送停止信号 */
                return ret;  
            }
           	          
        }
    }	
    if ((xfer->direction == i2c_trans_write) && (xfer->dataSize > 0))
    {
    	hdl_i2c_master_write(base, xfer->data, xfer->dataSize);
	}
    if ((xfer->direction == i2c_trans_read) && (xfer->dataSize > 0))
    {
       	hdl_i2c_master_read(base, xfer->data, xfer->dataSize);
	}
	#if (HDL_I2C_DEG == ON)
	DBG_MSG("\r\n=======i2c======\r\n");
	DBG_MSG("xfer->data:0x%x\r\n",*xfer->data);
	DBG_MSG("xfer->dataSize:0x%x\r\n",xfer->dataSize);
	DBG_MSG("xfer->direction:0x%x\r\n",xfer->direction);
	DBG_MSG("xfer->slaveAddress:0x%x\r\n",xfer->slaveAddress);
	DBG_MSG("xfer->subaddress:0x%x\r\n",xfer->subaddress);
	DBG_MSG("xfer->subaddressSize:0x%x\r\n",xfer->subaddressSize);
	DBG_MSG("=======i2c======\r\n");
	#endif
	return SUCCESS;	
}

int8_t hdl_i2c_write(I2C_Type *base,uint8_t dev_addr,uint8_t reg_addr,uint8_t *w_buf,uint8_t len)
{
	struct _i2c_transfer_info w_transfer;
	w_transfer.data = w_buf;
	w_transfer.dataSize = len;
	w_transfer.direction = i2c_trans_write;
	w_transfer.slaveAddress = dev_addr;
	w_transfer.subaddress= reg_addr;
	w_transfer.subaddressSize = 1;
	return i2c_master_transfer(base,&w_transfer);
}

int8_t hdl_i2c_read(I2C_Type *base,uint8_t dev_addr,uint8_t reg_addr,uint8_t *r_buf,uint8_t len)
{
	uint8_t val = 0;
	int8_t ret = FAIL;
	struct _i2c_transfer_info r_transfer;
	r_transfer.data = &val;
	r_transfer.dataSize = len;
	r_transfer.direction = i2c_trans_read;
	r_transfer.slaveAddress = dev_addr;
	r_transfer.subaddress= reg_addr;
	r_transfer.subaddressSize = 1;
	ret = i2c_master_transfer(base,&r_transfer);
	*r_buf = val;
	return ret;
}

int8_t hdl_i2c_init(I2C_Type *base)
{
	if(base == I2C1)
	{
		DBG_INFO("i2c1 gpio init\r\n");
		IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL, 1);
		IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA, 1);
		IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL, 0x70B0);
		IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA, 0X70B0);
	}
	base->I2CR &= ~(1<<7);	//turn off i2c

	base->IFDR = 0x15 << 0;		//640 div,103.125khz

	base->I2CR |= (1<<7);	//turn on i2c

	DBG_MSG("i2c 0x%lx init\r\n",base);
	return SUCCESS;
}
/////////////////////////////////////////////////////////////////////////////////////////
int8_t hdl_analog_i2c_start(void)
{
	hal_gpio_write(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN,GPIO_HIGH);
	hal_gpio_write(I2C_SCL_GPIO_PORT,I2C_SCL_GPIO_PIN,GPIO_HIGH);
	delayus(1);
	hal_gpio_write(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN,GPIO_LOW);
	delayus(1);
	hal_gpio_write(I2C_SCL_GPIO_PORT,I2C_SCL_GPIO_PIN,GPIO_LOW);
	delayus(1);
	return SUCCESS;
}

int8_t hdl_analog_i2c_stop(void)
{
	hal_gpio_write(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN,GPIO_LOW);
	hal_gpio_write(I2C_SCL_GPIO_PORT,I2C_SCL_GPIO_PIN,GPIO_HIGH);
	delayus(1);
	hal_gpio_write(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN,GPIO_LOW);
	return SUCCESS;
}

int8_t hdl_analog_i2c_waitack(void)
{
	hal_gpio_write(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN,GPIO_HIGH);
	delayus(1);
	hal_gpio_write(I2C_SCL_GPIO_PORT,I2C_SCL_GPIO_PIN,GPIO_HIGH);
	delayus(1);
	hal_gpio_write(I2C_SCL_GPIO_PORT,I2C_SCL_GPIO_PIN,GPIO_LOW);
	return SUCCESS;
}

int8_t hdl_analog_i2c_write(uint8_t data)
{
	uint8_t i = 0;
	for(i=0;i<8;i++)
	{
		if(data & 0x80)
		{
			hal_gpio_write(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN,GPIO_HIGH);
		}
		else
		{
			hal_gpio_write(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN,GPIO_LOW);
		}
		delayus(1);
		hal_gpio_write(I2C_SCL_GPIO_PORT,I2C_SCL_GPIO_PIN,GPIO_HIGH);
		delayus(1);
		hal_gpio_write(I2C_SCL_GPIO_PORT,I2C_SCL_GPIO_PIN,GPIO_LOW);
		data <<= 1;
	}
	return SUCCESS;
}


int8_t hdl_analog_i2c_init(I2C_Type *base)
{
	hal_gpio_StructInit gpio_struct;
	if(base == I2C1)
	{
		gpio_struct.direction = GPIO_OUT;
		gpio_struct.interruptMode = kGPIO_NoIntmode;
		gpio_struct.output = 1;
		hal_gpio_cfg(GPIO1,29, &gpio_struct);
		hal_gpio_cfg(GPIO1,28, &gpio_struct);
	}
}

struct hal_i2c_if g_hdl_i2c_if = 
{
	.i2c_init = hdl_analog_i2c_init,//hdl_i2c_init,//

	.i2c_write = hdl_i2c_write,
	.i2c_read = hdl_i2c_read,

	.i2c_analog_init = hdl_analog_i2c_init,
	.i2c_analog_start = hdl_analog_i2c_start,
	.i2c_analog_stop = hdl_analog_i2c_stop,
	.i2c_analog_wait_ack = hdl_analog_i2c_waitack,
	.i2c_analog_write = hdl_analog_i2c_write,
};